<template>
  <div :class="prefixCls">
    <TreeSelect
      v-bind="getAttrs"
      v-model:value="state"
      :treeData="treeDataRef"
      @click="handleFetch"
    >
      <template #[item]="data" v-for="item in Object.keys($slots)">
        <slot :name="item" v-bind="data || {}"></slot>
      </template>
      <template #notFoundContent v-if="loading">
        <span>
          <LoadingOutlined spin class="mr-1" />
          {{ t('component.form.apiSelectNotFound') }}
        </span>
      </template>
    </TreeSelect>
  </div>
</template>
<script lang="ts">
import {computed, defineComponent, onMounted, ref, unref, watch} from 'vue';
import {TreeSelect} from 'ant-design-vue';
import {isEmpty, isFunction} from '/@/utils/is';
import {propTypes} from '/@/utils/propTypes';
import {listToTree} from '/@/utils/helper/treeHelper';
import {useAttrs} from '/@/hooks/core/useAttrs';
import {useRuleFormItem} from '/@/hooks/component/useFormItem';
import {get, omit} from 'lodash-es';
import {LoadingOutlined} from '@ant-design/icons-vue';
import {useI18n} from '/@/hooks/web/useI18n';
import {TreeItem} from '/@/components/Tree';
import {useDesign} from '/@/hooks/web/useDesign';

export default defineComponent({
    name: 'MyTreeSelect',
    components: { TreeSelect, LoadingOutlined },
    // inheritAttrs: false,
    props: {
      value: {
        type: [Array, Object, String, Number] as PropType<Array<any> | object | string | number>,
      },
      labelValue: {
        type: [Array, Object, String, Number] as PropType<Array<any> | object | string | number>,
      },
      labelInValue: propTypes.bool,
      treeData: {
        type: Array as PropType<Recordable[] | TreeItem[]>,
        default: () => [],
      },
      api: {
        type: Function as PropType<(arg?: Recordable) => Promise<Recordable[] | TreeItem[]>>,
        default: null,
      },
      params: {
        type: Object as PropType<Recordable>,
        default: () => ({}),
      },
      isDisable: {
        type: Function as PropType<(node: Recordable) => boolean>,
        default: null,
      },
      resultField: propTypes.string.def(''),
      immediate: propTypes.bool.def(false),
      // 暂时不支持
      dictCode: propTypes.string,
      treeCheckable: propTypes.bool,
      treeDataSimpleMode: propTypes.bool.def(true),
      canSelectParent: propTypes.bool.def(true),

      // 后端返回的树结构
      //  id: 'id',
      //  children: 'children',
      //  pid: 'pid',
      fieldNames: propTypes.object.def({
        value: 'id',
        label: 'name',
        id: 'id',
        name: 'name',
        pid: 'pid',
        children: 'children'
      })
    },
    emits: ['options-change', 'change', 'click'],
    setup(props, { emit }) {
      const { prefixCls } = useDesign('my-tree-select');
      const { t } = useI18n();
      const attrs = useAttrs();

      const treeDataRef = ref<Recordable[]>(props.treeData);
      const isFirstLoad = ref<Boolean>(false);
      const loading = ref<Boolean>(false);

      const getAttrs = computed(() => {
        let propsData = {
          height: 400, // 虚拟滚动
          showSearch: true,
          treeNodeFilterProp: props.fieldNames.name || 'name',
          // fieldNames: {
          //   value: 'id',
          //   label: props.fieldNames.name || 'name',
          // },
          treeDataSimpleMode: false,
          dropdownStyle: { maxHeight: '300px' },
          popupClassName: 'my-tree-select-popup',
          getPopupContainer: () => document.body,
          ...unref(attrs),
          ...(props as Recordable),
        };
        return omit(propsData, 'treeData');
      });

      const [state] = useRuleFormItem(props);

      if (!isEmpty(props.dictCode)) {
        // const { initSelectTreeData } = useDict();
        // initSelectTreeData(treeDataRef, props.dictType, true);
      }

      watch(
        () => props.treeData,
        () => {
          treeDataRef.value = getTreeData(props.treeData);
          emit('options-change', unref(treeDataRef));
        },
      );

      watch(
        () => props.params,
        () => {
          isFirstLoad.value && fetch();
        },
        { deep: true },
      );

      watch(
        () => props.immediate,
        (v) => {
          v && !isFirstLoad.value && fetch();
        },
      );

      onMounted(async () => {
        if (props.treeData && props.treeData.length > 0) {
          treeDataRef.value = getTreeData(props.treeData);
        }
        if (props.immediate) {
          await fetch();
          isFirstLoad.value = true;
        }
      });

      async function fetch() {
        const { api } = props;
        if (!api || !isFunction(api)) return;
        treeDataRef.value = [];
        try {
          loading.value = true;
          let res = await api(props.params);
          if (props.resultField) {
            res = get(res, props.resultField) || [];
          }
          if (Array.isArray(res)) {
            treeDataRef.value = getTreeData(res);
          }
          emit('options-change', unref(treeDataRef));
        } catch (error) {
          console.warn(error);
        } finally {
          loading.value = false;
        }
      }

      function getTreeData(treeData: Recordable[]) {
        const { fieldNames } = unref(props)
        if (props.treeDataSimpleMode) {
          const tree = listToTree(treeData, {
            id: fieldNames.id || 'id',
            pid: fieldNames.pid || 'pid',
            children: fieldNames.children || 'children',
            callback: (parent, node) => {
              if (props.isDisable && node) {
                node.disabled = node.disabled || props.isDisable(node);
              }
              if (!props.canSelectParent && parent) {
                if (parent.children && parent.children.length > 0) {
                  parent.disabled = true;
                }
              }
            },
          });
          return tree
        }
        return treeData;
      }

      async function handleFetch() {
        if (!props.immediate && !unref(isFirstLoad)) {
          await fetch();
          isFirstLoad.value = true;
        }
        emit('click');
      }

      return { prefixCls, t, getAttrs, state, treeDataRef, loading, handleFetch };
    },
  });
</script>
<style lang="less">
  @prefix-cls: ~'@{namespace}-dark-switch';

  .@{prefix-cls} {
    width: 100%;
    .ant-select {
      width: 100%;
    }
  }

  .ant-select-dropdown.@{prefix-cls}-popup .ant-select-tree {
    .ant-select-tree-switcher {
      &.ant-select-tree-switcher-noop {
        width: 15px;
      }
      .ant-select-tree-switcher-icon svg {
        margin-top: -3px;
      }
    }
  }
</style>
